package com.franklin.validation.core.argument.resolvers;


import com.franklin.validation.core.annotations.ParamCheck;

import com.franklin.validation.core.cache.RequestMultiValueCache;
import com.franklin.validation.core.constants.Constants;
import com.franklin.validation.core.entity.CheckFormEntity;
import com.franklin.validation.core.exception.ParamCheckException;
import com.franklin.validation.core.interfaces.Rule;
import com.franklin.validation.core.utils.DataCheckUtil;
import com.franklin.validation.core.utils.ReflectionUtil;



import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.web.reactive.BindingContext;
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.Serializable;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Description : 提供WebFlux form data参数解析器
 * 快速开发初版，代码臃肿
 * Create By: Franklin
 * Date : 2020/6/5 15:05
 */
@AllArgsConstructor
@NoArgsConstructor
@Deprecated
public class ParamCheckHandlerArgumentResolver implements HandlerMethodArgumentResolver,Rule<CheckFormEntity,Object> {

    @Autowired
    private RequestMultiValueCache<String,String>  cache;


    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.hasParameterAnnotation(ParamCheck.class);
    }

    @Override
    public Mono<Object> resolveArgument(MethodParameter methodParameter, BindingContext bindingContext, ServerWebExchange serverWebExchange) {
        Class<?> returnType = methodParameter.getMethod().getReturnType();
        try {
            HttpHeaders headers = serverWebExchange.getRequest().getHeaders();
            String requestId = headers.get(Constants.REQUEST_ID).get(0);
            ParamCheck paramCheck = methodParameter.getParameterAnnotation(ParamCheck.class);
//            Map<String, List<String>> params = ParamsCache.getParams(requestId);
            Map<String, List<String>> params = this.cache.getParams(requestId);
            Class<?> parameterType = methodParameter.getParameterType();
            boolean isSysDefault = ReflectionUtil.isSystemDefaultValue(paramCheck.defaultValue());
            boolean paramEmpty = DataCheckUtil.isEmpty(params);
            if (paramCheck.required() && paramEmpty && isSysDefault){
                this.getError(paramCheck);
            }else if (!paramCheck.required() && paramEmpty && isSysDefault){
                return this.getResult(null,parameterType,paramCheck);
            }else if (paramCheck.required() && paramEmpty && !isSysDefault){
                return this.getResult(paramCheck.defaultValue(),parameterType,paramCheck);
            }
            List<String> values = params.get(paramCheck.value());
            CheckFormEntity checkFormEntity = new CheckFormEntity(values,parameterType,paramCheck,null);

            Mono<Object> value = this.getResult(this.check(checkFormEntity), parameterType,paramCheck);
            if (value != null) return value;
            return Mono.just(parameterType.newInstance());
        } catch (Exception e) {
            ParamCheckException paramCheckException = new ParamCheckException(e.getMessage());
            paramCheckException.setResultType(returnType);
            throw paramCheckException;
        }
    }

    private Mono<Object> getResult(Object values, Class<?> parameterType,ParamCheck paramCheck) {
        boolean isEmpty = DataCheckUtil.isEmpty(values);
        try {
            if (Collection.class.isAssignableFrom(parameterType)){
                if (isEmpty){
                    return Mono.just(new ArrayList<>());
                }
                List<String> list = (List<String>) values;
                if (paramCheck.type() == String.class){
                    return Mono.just(values);
                }else {
                    List<? extends Serializable> list1 = list.stream()
                            .map(str -> {
                                if (paramCheck.type() == Integer.class) {
                                    return Integer.parseInt(str);
                                } else if (paramCheck.type() == Long.class) {
                                    return Long.parseLong(str);
                                } else if (paramCheck.type() == Double.class) {
                                    return Double.parseDouble(str);
                                }
                                return str;
                            })
                            .collect(Collectors.toList());
                    return Mono.just(list1);
                }
            }else if (Number.class.isAssignableFrom(parameterType)){
                String value = DataCheckUtil.trim(values);
                if (Integer.class.isAssignableFrom(parameterType)){
                    if (isEmpty){
                        return Mono.just(0);
                    }
                    return Mono.just(Integer.parseInt(value));
                }else if (Long.class.isAssignableFrom(parameterType)){
                    if (isEmpty){
                        return Mono.just(0L);
                    }
                    return Mono.just(Long.parseLong(value));
                }else if (Double.class.isAssignableFrom(parameterType)){
                    if (isEmpty){
                        return Mono.just(new Double("0.0"));
                    }
                    return Mono.just(Double.parseDouble(value));
                }
            }else {
                if (isEmpty){
                    return Mono.just("");
                }
                return Mono.just(values);
            }
        } catch (NumberFormatException e) {
            this.getError(paramCheck);
        }
        return null;
    }

    @Override
    public Object check(CheckFormEntity input) {
        Class<?> parameterType = input.getParamType();
        ParamCheck paramCheck = input.getParamCheck();
        List<String> values = input.getValues();
        boolean isEmpty = DataCheckUtil.isEmpty(values);
        boolean isSysDefault = ReflectionUtil.isSystemDefaultValue(paramCheck.defaultValue());

        if (Collection.class.isAssignableFrom(parameterType)){
            if (!paramCheck.required() && isEmpty){
                return new ArrayList<>();
            }
            values = this.getStringStream(paramCheck, values)
                    .collect(Collectors.toList());
            isEmpty = DataCheckUtil.isEmpty(values);
            if (paramCheck.required() && isEmpty){
                return getError(paramCheck);
            }
            if (!paramCheck.required() && isEmpty){
                return values;
            }
            if (values.size() > paramCheck.max() || values.size() < paramCheck.min()){
                return getError(paramCheck);
            }
            return values;
        }else if (Number.class.isAssignableFrom(parameterType)){
            if (paramCheck.required()){
                //必填
                if (isEmpty){
                    if (isSysDefault){
                        this.getError(paramCheck);
                    }
                    return paramCheck.defaultValue();
                }
            }else {
                //非必填
                if (isEmpty){
                    if (isSysDefault){
                        return "";
                    }
                    return paramCheck.defaultValue();
                }
            }
            //成功
            String value = values.get(0);
            value = DataCheckUtil.trim(value);
            boolean hasPattern = DataCheckUtil.isNotEmpty(paramCheck.pattern());
            boolean patternPass = false;
            if (hasPattern){
                patternPass = Pattern.compile(paramCheck.pattern()).matcher(value).matches();
            }else {
                patternPass = true;
            }
            if (!patternPass){
                if (isSysDefault){
                    this.getError(paramCheck);
                }
                return paramCheck.defaultValue();
            }
            return value;
        }else {
            if (!paramCheck.required() && isEmpty && isSysDefault){
                return "";
            }
            String value = null;
            if (!isEmpty){
                value = getStringStream(paramCheck, values).collect(Collectors.joining(","));
            }
            isEmpty = DataCheckUtil.isEmpty(value);
            if (paramCheck.required()){
                //必填
                if (isEmpty){
                    //为空
                    if (isSysDefault){
                        this.getError(paramCheck);
                    }
                    return paramCheck.defaultValue();
                }
            }else {
                //非必填
                if(isEmpty){
                    if (isSysDefault){
                        return "";
                    }else {
                        return paramCheck.defaultValue();
                    }
                }
            }
            return value;
        }
    }

    private Stream<String> getStringStream(ParamCheck paramCheck, List<String> values) {
        boolean isSysDefault = ReflectionUtil.isSystemDefaultValue(paramCheck.defaultValue());
        boolean hasPattern = DataCheckUtil.isNotEmpty(paramCheck.pattern());
        return values
                .stream()
                .map(value -> {
                        boolean patternPass = false;
                        boolean lengthPass = false;
                        if (hasPattern && String.class == paramCheck.type()) {
                            patternPass = Pattern.compile(paramCheck.pattern()).matcher(value).matches();
                        } else if (hasPattern && Date.class == paramCheck.type()) {
                            patternPass = DataCheckUtil.checkDate(value, paramCheck.pattern());
                        } else {
                            patternPass = true;
                        }
                        if (String.class == paramCheck.type()){
                            String str = value;
                            if (str.length() <= paramCheck.max() && str.length() >= paramCheck.min()){
                                lengthPass = true;
                            }
                        }else {
                            lengthPass = true;
                        }
                        if (!patternPass || !lengthPass) {
                            if (isSysDefault){
                                this.getError(paramCheck);
                            }
                            return paramCheck.defaultValue();
                        }
                        return value;
                    });
    }

    private Object getError(ParamCheck paramCheck) {
        if (DataCheckUtil.isNotEmpty(paramCheck.message())){
            throw new ParamCheckException(Constants.MESSAGE + paramCheck.message());
        }else {
            throw new ParamCheckException(paramCheck.value());
        }
    }
}
